package com.woniu.util;

import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 堆是公有的，存的是公共资源：成员变量需要在方法中共享，所以放在堆中
 * 栈是私有的，每个方法的成员变量都放在该方法自己私有的栈中
 * 方法区存的是静态资源
 *
 * 非原子性代码，在多线程情况下，都是不安全的代码（有bug）
 *
 * 什么是非原子性代码：
 *      计算类代码，--，++
 * 1:出现非原子性代码，对共享变量操作（如果操作的是局部变量，那么不加锁也能保证安全问题）
 * 2：如果同一时间只有一个线程进入，那么可以加锁实现
 *
 * 怎么解决非原子性代码带来的多线程情况下安全性问题？
 * 解决办法只有一种：加锁
 *
 * synchronized（jdk锁） 作用：加在方法上，使方法变成一个 同步方法
 *                                  同步方法：同一时间只允许一个线程进入，其他线程必须等待上一线程走完才能进入
 * synchronized 原理：当一个线程进入该方法时，会在方法上贴一个自己线程的标签。直到走完，会去掉标签
 *                   其他线程会等待上一个线程走完
 *                   sleep：会争夺 cpu资源，wait：放弃争夺 cpu资源
 * synchronized 锁升级的概念：有空自行了解
 *
 * lock 锁（也属于jdk锁）：一个接口，主要应用的两个实现类是：
 *                                              （1）重入锁 ReentrantLock
 *                                              （2）读写锁 （I/0 读写时用）
 */
@Component
public class Count {
    public int count = 10000;  //共享资源
    ReentrantLock lock = new ReentrantLock();
    public synchronized void sub() {
        if(count > 0){
            count --;
            // 会被翻译成以下三条语句：
            // 1：读取原来的值；
            // 2:对值减一
            // 3：赋新的值
        }

    }
    public void subLock(){
        try {
            //lock.lock();    //加锁
            //参数1：数值，参数2：单位
            //线程在指定时间内，会去尝试获取锁，能：返回 true，不能：返回 false
            if(lock.tryLock(2, TimeUnit.SECONDS)){
                if(count > 0){
                    count --;
                    Thread.sleep(2000);
                    // 1：读取原来的值；
                    // 2:对值减一
                    // 3：赋新的值
                }
            }else {
                System.out.println("线程没有拿到锁！");
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //判断是不是线程自己的锁；避免上述情况：拿到锁的线程sleep，其他没拿到锁的线程释放自己没有的锁从而抛出异常
            if(lock.isHeldByCurrentThread()){
                lock.unlock();  //释放锁，一定要保证实行，否则成为死锁
            }

        }

    }
}
